package com.cellulam.properties;

import com.cellulam.framework.core.exceptions.SysException;
import com.cellulam.framework.core.model.DataResult;
import com.cellulam.framework.core.rest.RestClient;
import com.fasterxml.jackson.core.type.TypeReference;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.lang.reflect.Type;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;

@Component
@Slf4j
public class SystemConfig {
    @Autowired
    private RestClient restClient;

    @Autowired
    private SystemProperties systemProperties;

    private final LoadingCache<String, String> systemConfigCache = Caffeine.newBuilder()
            .maximumSize(10_000)
            .expireAfterWrite(24, TimeUnit.HOURS)
            .refreshAfterWrite(30, TimeUnit.MINUTES)
            .build(key -> getValueImmediately(key));

    private final LoadingCache<String, List<BasicConfig>> systemConfigsCache = Caffeine.newBuilder()
            .maximumSize(10_000)
            .expireAfterWrite(24, TimeUnit.HOURS)
            .refreshAfterWrite(30, TimeUnit.MINUTES)
            .build(key -> getConfigsImmediately(key));

    /**
     * 通过分类code拿配置集合
     * 不走本地缓存
     *
     * @param categoryCode
     * @return
     */
    public List<BasicConfig> getConfigsImmediately(String categoryCode) {
        if (StringUtils.isEmpty(categoryCode)) {
            throw new SysException("分类code 不能为空");
        }

        try {
            DataResult<List<BasicConfig>> result = this.restClient.getForObject(systemProperties.getCellulamBasicHost() + "/v1/configuration/sub?categoryCode={0}",
                    new TypeReference<DataResult<List<BasicConfig>>>() {
                    }, categoryCode);

            if (result.isOk()) {
                return result.getData();
            }

        } catch (Exception e) {
            log.error("获取配置失败异常, categoryCode: {}", categoryCode, e);
        }
        return null;
    }


    /**
     * 不走本地缓存
     *
     * @param code
     * @return
     */
    public String getValueImmediately(String code) {
        if (StringUtils.isEmpty(code)) {
            return null;
        }

        try {
            return this.restClient.getForResult(systemProperties.getCellulamBasicHost() + "/v1/configuration?code={0}",
                    String.class, code);
        } catch (Exception e) {
            log.error("获取value异常, code: {}", code, e);
            return null;
        }
    }


    /**
     * 通过分类code拿配置集合
     *
     * @param categoryCode
     * @return
     */
    public List<BasicConfig> getConfigs(String categoryCode) {
        if (StringUtils.isEmpty(categoryCode)) {
            return null;
        }
        return this.systemConfigsCache.get(categoryCode);
    }


    public String getValue(String code) {
        if (StringUtils.isEmpty(code)) {
            return null;
        }
        return this.systemConfigCache.get(code);
    }

    public String getValue(String code, String defaultValue) {
        return this.getValue(code, defaultValue, x -> x);
    }

    /**
     * 转int，默认为null
     *
     * @param code
     * @return
     */
    public Integer getIntValue(String code) {
        return this.getIntValue(code, null);
    }

    public Integer getIntValue(String code, Integer defaultValue) {
        return this.getValue(code, defaultValue, Integer::parseInt);
    }

    public Double getDoubleValue(String code) {
        return this.getDoubleValue(code, null);
    }

    public Double getDoubleValue(String code, Double defaultValue) {
        return this.getValue(code, defaultValue, Double::parseDouble);
    }

    public <R> R getValue(String code, R defaultValue, Function<String, R> converter) {
        try {
            String value = this.getValue(code);
            if (StringUtils.isEmpty(value)) {
                return defaultValue;
            }

            return converter.apply(value);
        } catch (Exception e) {
            log.error("", e);
            return defaultValue;
        }
    }
}
